
package scalaExec.gui;

import java.util.StringTokenizer;

/* 
The Scala Interpreter implements all defined names (vars, vals, functions, modules)
as members of synthetically generated classes and thereafter implements
an elaborate mechanism to realize the continuation of the computation state.
This mechanism is based on executing these classes and importing these fields.
 
 However, for some reason the JIT compiler is ineffective in field access
 and thus numerical scripts executed directly at the Scala interpreter are much
 slower than code placed inside functions/classes that utilizes local variables instead.
 
 For this rationale, we introduce in ScalaLab a conveniency construct,
 the "export var" or "export val" statements.
 When these statements exist in a script, ScalaLab automatically places the 
 whole script code in the "userCompute$$"  function, and
 keeps global definitions as fields only for the "exported" vars/vals.
 
 The consequence is a dramatic speedup of numerical scripts, about 20-40 times faster.
  
*/

//   this class detects if the user has declared any vars with
//   "export var" or "export val" that means that these variables ONLY are kept in the binding.
public class FastScriptingMode {
    public static boolean isFastScripting(String evalStr)  {
        boolean requestFastScripting = false;
        StringTokenizer strtok = new StringTokenizer(evalStr, " \t\n}{;");
        while (strtok.hasMoreTokens())  {
            String tok = strtok.nextToken();
            if (tok.equals("export")) {
                String nextTok = strtok.nextToken(); // should be "var" 
                if (nextTok.equals("var")==true )  {
                   requestFastScripting = true;
                   break;
                }
            }   // tok== "export"
        }  // hasMoreTokens
            return requestFastScripting;
    }
    
    // transform the script with "export var" or "export val" statements to an
    // equivalent one, that declares only the exported variables globally as fields 
    // and places the script code within the userCompute$$  synthetically
    // generated function, in order to present the script variables as local
    // variables to the JIT compiler
    public static String  transformScriptToFast(String evalStr) {
        StringBuilder compiledInFunctionText = new StringBuilder();   // the code that is placed within the userCompute$$ function
        StringBuilder exportedVarsAsFieldsText = new StringBuilder();   // the code to define the exported vars/vals as fields
        
        String [] lines = evalStr.split("\n");
        int numLines = lines.length;
        for (int lineNo=0; lineNo<numLines; lineNo++ )  {
            String currentLine = lines[lineNo];
            String []multipleCommands = currentLine.split(";");  // split multiple commands separated with semicolonn (';')
            int numCommands = multipleCommands.length;   // number of multiple commands
            for (int commandNo=0; commandNo<numCommands; commandNo++)  {  // for each '; ' separated command
            // process each line separately
         StringTokenizer strtok = new StringTokenizer(multipleCommands[commandNo], " \t");
         while (strtok.hasMoreTokens()) {
            String tok = strtok.nextToken();
            if (tok.equals("export")) {
                String nextTok = strtok.nextToken(); // should be "var" or "val"
                String nextToExport = strtok.nextToken(); 
                if (nextTok.equals("var")!=true && nextTok.equals("val")!=true)  {
                    return evalStr; // an error happened, perhaps "export" is within a comment, return the script unaltered
                }
                else {
                    exportedVarsAsFieldsText.append("var  "+nextToExport +":AnyRef = _  ;\n");   // add a default initialization definition as field of the Script class
                    compiledInFunctionText.append(nextToExport);   // do not use "var" definition again for the variable, since it will be defined globally as a field
                        // consume tokens on that line
                     while (strtok.hasMoreTokens()) {
                         tok = strtok.nextToken();
                         compiledInFunctionText.append(tok);
                      }
                    
                     compiledInFunctionText.append(".asInstanceOf[AnyRef] ;");
                    
                }
            }
              else   // not "export" simply copy text
                  compiledInFunctionText.append(tok+" ");
          }  // has more tokens
         compiledInFunctionText.append("; ");
        }  // for each '; ' separated command
      
         compiledInFunctionText.append("\n");
        }  // all lines 
        String allcompiledInFunctionText = exportedVarsAsFieldsText.toString()+"\n\n"+
                "def  userCompute$$ = { "+
                compiledInFunctionText.toString()+ " \n}\n\n"+
                "userCompute$$  ; \n";

        return allcompiledInFunctionText;        
        }

}
